// Copyright DustedPixels.com 2008. All rights reserved.

package com.dustedpixels.jasmin.unit.compiler;

import java.util.Arrays;
import java.util.List;

import org.apache.commons.lang.StringUtils;

/**
 * @author micapolos@gmail.com (Michal Pociecha-Los)
 */
public final class ProcessCodegenImpl implements ProcessCodegen {
  private final static int INDENT_SIZE = 2;
  
  private StringBuffer buffer;
  private boolean emptyLine; 
  private int indent;
  
  public String generateCode(Unit unit) {
    buffer = new StringBuffer();
    emptyLine = true;
    indent = 0;
    
    try {
      // Write file comments
      out("// This file was auto-generated");
      newline();
      
      // Write package declaration
      if (unit.packageName != null) {
        out("package " + unit.packageName + ";");
        newline();
      }
      
      // Write imports
      boolean importsUnit = false;
      for (String anImport : unit.imports) {
        out("import " + anImport + ";");
        importsUnit |= anImport.equals("com.dustedpixels.jasmin.unit.Process");
      }
      if (!importsUnit && 
          (unit.packageName == null || 
           !unit.packageName.equals("com.dustedpixels.jasmin.unit"))) {
        out("import com.dustedpixels.jasmin.unit.Process;");
      }
      newline();
      
      // Write class declaration
      out("public final class " + unit.name + " implements Process {");
      outBegin();
      
      // Write field lines
      for (FieldLine fieldLine : unit.fieldLines) {        
        outMultiline(fieldLine.code);
      }
      newline();
      
      // Write constructor
      if (unit.constructorLines != null) {
        out("public " + unit.name + "(" + unit.constructorArgs + ") {");
        for (ConstructorLine constructorLine : unit.constructorLines) {
          out(constructorLine.code);
        }
        out("}");
        newline();
      } 
      
      // Write update method
      out("public void run(int __cycles) {");
      
      outBegin();
      for (FieldLine fieldLine : unit.fieldLines) {
        if (!fieldLine.code.contains(" final ") && !fieldLine.code.contains(" static ")) {
          List<String> parts = Arrays.<String>asList(StringUtils.split(fieldLine.code.replace(";", " "), " "));
          String type = parts.get(parts.indexOf(fieldLine.name) - 1);
          out(type + " " + fieldLine.name + " = this." + fieldLine.name + ";");
        }
      }
      out("while (__cycles-- != 0) {");

      for (UpdateEndPointsLine updateEndPointsLine : unit.updateEndPointsLines) {
        out(updateEndPointsLine.code);
      }
        
      out("}");
      for (FieldLine fieldLine : unit.fieldLines) {
        if (!fieldLine.code.contains(" final ") && !fieldLine.code.contains(" static ")) {
          out("this." + fieldLine.name + " = " + fieldLine.name + ";");
        }
      }
      outEnd();

      out("}");

      outEnd();
      out("}");
      
      return buffer.toString();
    } finally {
      buffer = null;
    }
  }
  
  private void out(String line) {
    if (emptyLine) for (int i = 0; i < indent * INDENT_SIZE; i++) buffer.append(" ");
    buffer.append(line);
    newline();
  }
  
  private void outMultiline(String text) {
    String[] lines = StringUtils.split(text, '\n');
    for (String line : lines) out(line);
  }
  
  private void newline() {
    buffer.append("\n");
    emptyLine = true;
  }
  
  private void outBegin() {
    indent++;
  }
  
  private void outEnd() {
    indent--;
  }
}
